import { _decorator, Component, Node, Vec3, Vec2, RigidBody2D, CircleCollider2D, Animation, director, Sprite } from 'cc';
import { JoystickController } from './JoystickController';
import { GameManager } from '../GameManager';
import { EventManager, GameEventType } from '../Core/EventManager';

const { ccclass, property } = _decorator;

enum PlayerState {
    IDLE = 'idle',
    MOVE = 'move',
    ATTACK = 'attack',
    HIT = 'hit',
    DEATH = 'death'
}

@ccclass('PlayerController')
export class PlayerController extends Component {
    @property(JoystickController)
    private joystick: JoystickController = null;
    
    @property
    private moveSpeed: number = 200;
    
    @property
    private health: number = 100;
    
    @property
    private maxHealth: number = 100;
    
    @property(Animation)
    private anim: Animation = null;
    
    private _currentState: PlayerState = PlayerState.IDLE;
    private _moveDirection: Vec3 = new Vec3();
    private _rigidBody: RigidBody2D = null;
    private _isInvincible: boolean = false;
    private _invincibleTime: number = 1.0;
    private _faceRight: boolean = true;
    
    start() {
        // Initialize components
        this._rigidBody = this.getComponent(RigidBody2D);
        
        // Register event listeners
        this._registerEvents();
    }
    
    update(deltaTime: number) {
        // Check game state from GameManager's public properties
        if (GameManager.instance.isGameOver) {
            return;
        }
        
        this._handleMovement(deltaTime);
        this._updateAnimation();
    }
    
    private _registerEvents(): void {
        // Register player damage event
        EventManager.instance.on(GameEventType.PLAYER_DAMAGE, this.takeDamage, this);
    }
    
    private _handleMovement(deltaTime: number): void {
        // Get joystick direction
        const moveDir = this.joystick.moveDirection;
        
        // Apply movement
        if (!moveDir.equals(Vec2.ZERO)) {
            // Convert Vec2 to Vec3 for movement
            this._moveDirection.x = moveDir.x;
            this._moveDirection.y = moveDir.y;
            this._moveDirection.z = 0;
            
            // Normalize and multiply by speed and deltaTime
            const normalizedDir = this._moveDirection.normalize();
            const movement = normalizedDir.multiplyScalar(this.moveSpeed * deltaTime);
            
            // Update position
            const newPos = this.node.position.clone().add(movement);
            this.node.setPosition(newPos);
            
            // Update scale to face move direction
            this._updateScale(moveDir);
            
            // Update state
            this._currentState = PlayerState.MOVE;
        } else {
            // Not moving, set to idle
            this._currentState = PlayerState.IDLE;
        }
    }
    
    private _updateScale(direction: Vec2): void {
        if (direction.equals(Vec2.ZERO)) return;
        
        // Check if scale needs to be updated
        if (direction.x > 0 && !this._faceRight) {
            // Moving right, set X scale to 1
            const currentScale = this.node.scale.clone();
            this.node.setScale(new Vec3(1, currentScale.y, currentScale.z));
            this._faceRight = true;
        } else if (direction.x < 0 && this._faceRight) {
            // Moving left, set X scale to -1
            const currentScale = this.node.scale.clone();
            this.node.setScale(new Vec3(-1, currentScale.y, currentScale.z));
            this._faceRight = false;
        }
    }
    
    private _updateAnimation(): void {
        // Handle animation state transitions
        const animName = this._currentState.toLowerCase();
        
        // Only change animation if needed
        if (this.anim && !this.anim.getState(animName).isPlaying) {
            this.anim.play(animName);
        }
    }
    
    public takeDamage(damage: number): void {
        if (this._isInvincible || GameManager.instance.isGameOver) return;
        
        this.health -= damage;
        
        // Clamp health
        if (this.health < 0) this.health = 0;
        
        // Flash effect to indicate damage
        this._playHitEffect();
        
        // Check for death
        if (this.health <= 0) {
            this._die();
        } else {
            // Set invincibility frames
            this._setInvincible(this._invincibleTime);
        }
        
        // Emit event for UI updates
        EventManager.instance.emit(GameEventType.PLAYER_DAMAGE, this.health, this.maxHealth);
    }
    
    private _playHitEffect(): void {
        // Play hit animation
        this._currentState = PlayerState.HIT;
        
        // Flash effect
        const sprite = this.node.getChildByName('Sprite');
        if (sprite) {
            const spriteComp = sprite.getComponent(Sprite);
            if (spriteComp) {
                // Manual flash effect using setTimeout
                let flashCount = 4;
                let isVisible = false;
                
                const flashInterval = setInterval(() => {
                    isVisible = !isVisible;
                    sprite.active = isVisible;
                    
                    flashCount--;
                    if (flashCount <= 0) {
                        clearInterval(flashInterval);
                        sprite.active = true;
                    }
                }, 100);
            }
        }
    }
    
    private _setInvincible(duration: number): void {
        this._isInvincible = true;
        
        // Reset invincibility after duration
        setTimeout(() => {
            this._isInvincible = false;
        }, duration * 1000);
    }
    
    private _die(): void {
        this._currentState = PlayerState.DEATH;
        
        // Play death animation
        if (this.anim) {
            this.anim.play('death');
        }
        
        // Disable controls
        this.enabled = false;
        
        // Emit game over event
        EventManager.instance.emit(GameEventType.PLAYER_DEATH);
        GameManager.instance.gameOver();
    }
    
    public getHealth(): number {
        return this.health;
    }
    
    public getMaxHealth(): number {
        return this.maxHealth;
    }
    
    public heal(amount: number): void {
        this.health += amount;
        
        // Clamp health to max
        if (this.health > this.maxHealth) {
            this.health = this.maxHealth;
        }
        
        // Emit event for UI updates
        EventManager.instance.emit(GameEventType.PLAYER_DAMAGE, this.health, this.maxHealth);
    }
    
    onDestroy() {
        // Unregister event listeners
        EventManager.instance.off(GameEventType.PLAYER_DAMAGE, this.takeDamage, this);
    }
} 